/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2018年11月12日
 * V4.0
 */
package com.jphenix.servlet.common;

import com.jphenix.driver.serialno.FSN;
import com.jphenix.kernel.baseobject.instanceb.ABase;
import com.jphenix.kernel.objectloader.interfaceclass.IBean;
import com.jphenix.service.db.common.instancea.DBQuery;
import com.jphenix.service.db.common.instancea.DBQueryExt;
import com.jphenix.service.db.datamanager.instancea.DataManager;
import com.jphenix.service.db.datamanager.interfaceclass.IDataManager;
import com.jphenix.service.db.mem.MemoryDbService;
import com.jphenix.share.tools.JCECrpUtil;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.standard.beans.IBase;
import com.jphenix.standard.db.IDBQuery;
import com.jphenix.standard.db.IDBQueryExt;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.script.IScriptBeanExtra;
import com.jphenix.standard.serialno.ISN;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 动作类跟服务类共同的父类
 * 用来提供数据库操作的方法
 * 
 * 参数主键值对照表：
 * 
 * 实际使用主键  传入的对照容器主键（已废弃，以后不再使      配置文件中的主键                说明
 * jndiName             db_jndi_name                             jndi_name                 JNDI模式中命名空间的名字（如果存在该值，则不需要其它值，可以存在登录名，密码值。必须存在source_type值）
 * sourceType           db_source_type                           source_type               在JNDI模式中，无法识别目标数据库类型，需要指定目标数据库的类型，目前支持以下类型：oracle,h2,mssql,mysql
 * name                 name                                     父节点名                  数据源主键名
 * title                title                                    attr:title                数据源说明
 * databaseUrl          db_url                                   node:url                  数据库连接字符串
 * user                 db_user                                  user                      登录账号
 * password             db_pwd                                   pwd                       登录密码
 * db_source            db_source                                db_source                 当前数据源未别名，真实数据源主键名为该值
 * driverClassName      db_driver_class_name                     driver                    驱动类路径
 * defaultTimeOut       db_time_out                              timeout                   数据库占用超时时间（毫秒）
 * defaultNotInUseTime  db_not_in_use_time                       notinuse                  数据库连接空闲超时时间（毫秒）
 * maxLoadCount         db_max_load_count                        maxloadcount              最大加载资源数量
 * autocommit           db_auto_commit                           autocommit                是否自动提交
 * initScript           init_script                              init_script               初始化数据库连接时需要执行的语句
 * replaceFilePathChar  replace_file_path_char                   replace_file_path_char    Access中替换文件路径的关键字字符
 * paraStatementType    --                                       paraStatementType         构建事务时的类型
 *                                                                                         
 * db_log_no_out_console  日志是否不输出到控制台
 * db_log_file_key        日志是否写入自定义日志文件  该值是自定义日志文件的文件名（不包含扩展名）
 * 
 * 
 * 
 * 2018-12-06 修改了获取表状态服务的方法
 * 2019-01-08 增加了建立数据库连接后，自动执行的脚本数据，主要用在mysql上，如果mysql用了utf8mb4编码
 *            ，又无法修改数据库启动配置，那只好在建立连接时执行设置环境变量语句：SET NAMES utf8mb4
 *            可以将这语句放到<init_script></init_script>段中
 * 2019-01-11 修改了设置init_script参数错误
 * 2019-03-06 增加了设置指定数据源信息方法；增加了获取指定数据源信息方法
 * 2019-03-15 增加了数据库操作日志输出到自定义日志文件中的参数信息db_log_file_key
 *            修改了一处错误又
 * 2019-04-24 完善了指定数据源屏蔽日志功能
 * 2019-06-18 修改了数据库操作扩展类的构建方法，由于扩展类发生了变化
 * 2019-06-26 修改了序列号生成器的构造方法
 * 2019-08-12 在数据源中放入数据源中文说明（标题）
 * 2019-11-05 增加了临时表操作方法
 * 2019-11-06 增加了传入内存数据库操作时间，支持持续保持指定临时表。也支持无保持，到达超时时间自动删除临时表
 * 2019-11-07 增加了一些常用的临时表操作方法，重构了连接数据库配置信息模块
 *            去掉了数据库配置参数的中间标识代码，只保留了数据库连接池标识代码，配置文件中的标识代码,详见顶部注释中的参数主键值对照表
 *            修改了漏掉的兼顾之前的中间数据库配置信息代码
 * 2019-11-08 修改了内存数据库JDBC连接字符串，去掉了落地功能
 * 2019-11-18 创建指定临时表插入数据
 * 2019-12-06 在脚本中支持另写两个方法，一个是在执行中发生异常时调用的方法doException。一个是无论执行正常结束，异常结束时都调用的方法doFinally
 * 2019-12-09 为doException和doFinally增加了传入参数
 * 2019-12-26 增加了JNDI模式配置
 * 
 * @author MBG
 * 2018年11月12日
 */
@ClassInfo({"2019-12-26 16:59","动作类跟服务类共同的父类"})
public class BaseParent extends ABase implements IScriptBeanExtra {

	private ISN sn = null; //主键生成器

	//数据库操作类容器
	private Map<String,IDBQuery> dbMap = new HashMap<String,IDBQuery>();
	
	public  Boolean hasMemDb          = null;  //是否允许内存数据库   null未初始化  true允许  false不允许（没有H2数据库包）
	private MemoryDbService memDbs    = null;  //内存数据库服务

	private final String memDbSource = "_memory_db_"; //数据源主键

	/**
	 * 构造函数
	 * @author MBG
	 */
	public BaseParent() {
		super();
	}

	/**
	 * 获取主键生成器器
	 * @return 主键生成器
	 */
	public ISN getSn() {
		if(sn==null) {
			//主键长度
			int snSize = sint(prop.getParameter("db/sn_size"));
			if(snSize<1) {
				snSize = 20;
			}
			//主键头
			String head = str(prop.getParameter("db_ext/head"));
			if(head.length()<1) {
				head =  _beanFactory.getProjectPin();
			}
			if(head.length()<1) {
				head = "AP";
			}
			sn = FSN.newInstance(head,snSize);
		}
		return sn;
	}

	/**
	 * 获取一个新的主键值
	 * @return 新的主键值
	 */
	public String nid() {
		return getSn().getSID();
	}
	
	/**
	 * 是否存在内存数据库
	 * @return 是否存在内存数据库
	 * 2019年11月5日
	 * @author MBG
	 */
	public boolean hasMemoryDb() {
		if(hasMemDb==null) {
			try {
				Class.forName("org.h2.Driver");
				hasMemDb = true;
			}catch(Exception e) {
				hasMemDb = false;
			}
		}
		return hasMemDb;
	}
	
	/**
	 * 创建指定临时表插入数据
	 * @param tableName    临时表的表名
	 * @param fieldNames   临时表中字段名序列
	 * @param rs           需要放入的记录集
	 * @param outTime      临时表自动删除超时时间（单位：秒）  为空时，设置默认超时时间。为-1时，当前进程永久临时表
	 * @param isAppend     是否增量插入数据（为false时，自动清空之前的表数据，再插入新的数据）
	 * @throws Exception   伊克塞普森
	 * 2019年11月18日
	 * @author MBG
	 */
	public void put(
		 	 String                   tableName
			,String[]                 fieldNames
			,List<Map<String,String>> rs
			,Long                     outTime
			,boolean                  isAppend) throws Exception {
		//获取内存数据库操作服务
		MemoryDbService mem = memDbs();
		if(mem==null) {
			return;
		}
		mem.put(tableName,fieldNames,rs,outTime,isAppend);
	}
	
	/**
	 * 将记录集放入内存数据库
	 * @param fieldNames  字段名数组
	 * @param rs          记录集
	 * @param indexFields 需要创建索引的字段数组
	 * @param outTime     临时表未访问超时时间（单位：秒）
	 * @return            记录集主键（临时表名）
	 * @throws            异常
	 * 2019年11月5日
	 * @author MBG
	 */
	public String memPut(
			 String[]                 fieldNames
			,List<Map<String,String>> rs
			,String[]                 indexFields
			,Long                     outTime) throws Exception {
		//获取内存数据库操作服务
		MemoryDbService mem = memDbs();
		if(mem==null) {
			return null;
		}
		return mem.put(fieldNames,rs,indexFields,outTime);
	}
	
	/**
	 * 将记录集放入内存数据库
	 * @param fieldNames  字段名数组
	 * @param rs          记录集
	 * @param indexFields 需要创建索引的字段数组
	 * @param outTime     临时表未访问超时时间（单位：秒）
	 * @return            记录集主键（临时表名）
	 * @throws            异常
	 * 2019年11月5日
	 * @author MBG
	 */
	public String memPut(
			 String[]                 fieldNames
			,List<Map<String,String>> rs
			,String[]                 indexFields) throws Exception {
		return memPut(fieldNames,rs,indexFields,null);
	}
	
	/**
	 * 将记录集放入内存数据库
	 * @param fieldNames 字段名数组
	 * @param rs         记录集
	 * @param outTime    临时表未访问超时时间（单位：秒）
	 * @return           记录集主键（临时表名）
	 * @throws           异常
	 * 2019年11月5日
	 * @author MBG
	 */
	public String memPut(String[] fieldNames,List<Map<String,String>> rs,Long outTime) throws Exception {
		return memPut(fieldNames,rs,null,outTime);
	}
	
	/**
	 * 将记录集放入内存数据库
	 * @param fieldNames 字段名数组
	 * @param rs         记录集
	 * @return           记录集主键（临时表名）
	 * @throws           异常
	 * 2019年11月5日
	 * @author MBG
	 */
	public String memPut(String[] fieldNames,List<Map<String,String>> rs) throws Exception {
		return memPut(fieldNames,rs,null,null);
	}
	
	
	/**
	 * 将记录集放入内存数据库
	 * @param rs         记录集
	 * @return           记录集主键（临时表名）
	 * @param outTime    临时表未访问超时时间（单位：秒）
	 * @throws Exception 异常
	 * 2019年11月6日
	 * @author MBG
	 */
	public String memPut(List<Map<String,String>> rs,Long outTime) throws Exception {
		if(rs==null || rs.size()<1) {
			return null;
		}
		return memPut(BaseUtil.getMapKeys(rs.get(0)),rs,null,outTime);
	}
	
	/**
	 * 将记录集放入内存数据库
	 * @param rs         记录集
	 * @return           记录集主键（临时表名）
	 * @throws Exception 异常
	 * 2019年11月6日
	 * @author MBG
	 */
	public String memPut(List<Map<String,String>> rs) throws Exception {
		if(rs==null || rs.size()<1) {
			return null;
		}
		return memPut(BaseUtil.getMapKeys(rs.get(0)),rs,null,null);
	}
	
	/**
	 * 内存数据库中是否存在指定临时表
	 * @param tableName 表名
	 * @return 是否存在指定临时表
	 * 2019年11月5日
	 * @author MBG
	 */
	public boolean memExists(String tableName) {
		MemoryDbService mem = memDbs(); //获取内存数据库服务类
		if(mem==null) {
			return false;
		}
		return mem.exists(tableName);
	}
	
	/**
	 * 获取内存数据库操作类
	 * 注意：调用该方法后，该临时表会持续保持，直到无访问超时后自动删除
	 * @param tableName 表名
	 * @return          内存数据库操作类
	 * 2019年11月5日
	 * @author MBG
	 */
	public IDBQuery memDb(String tableName) {
		MemoryDbService mem = memDbs(); //获取内存数据库服务类
		if(mem==null) {
			return null;
		}
		return mem.db(tableName);
	}
	
	
	
	
	/**
	 * 获取内存数据库操作类
	 * 注意：调用该方法并不会延长该临时表超时时间，到达指定时间后该临时表会自动删除
	 * @return 内存数据库操作类
	 * 2019年11月5日
	 * @author MBG
	 */
	public IDBQuery memDb() {
		MemoryDbService mem = memDbs(); //获取内存数据库服务类
		if(mem==null) {
			return null;
		}
		return mem.db();
	}
	
	
	/**
	 * 获取内存数据库服务
	 * @return 内存数据库服务
	 * @throws Exception 异常
	 * 2019年11月6日
	 * @author MBG
	 */
	protected MemoryDbService memDbs() {
		if(memDbs==null) {
			if(!hasMemoryDb()) {
				return null;
			}
			try {
				if(_beanFactory.beanExists(MemoryDbService.class)) {
					memDbs = _beanFactory.getObject(MemoryDbService.class,this);
				}else {
					//数据库管理类
					IDataManager dm = getBean(IDataManager.class);

					Map<String,String> infoMap = new HashMap<String,String>();
					infoMap.put("name",memDbSource);
					infoMap.put("title","内存数据库");
					infoMap.put("databaseUrl","jdbc:h2:mem:buffer");
					infoMap.put("db_source",memDbSource); //设置当前数据源未别名，真实数据源主键名为该值
					infoMap.put("driverClassName","org.h2.Driver");
					infoMap.put("db_log_no_out_console","true"); //不输出日志到控制台，否则插入数据那块日志量太大，并且没啥用
					infoMap.put("autocommit","true");
					infoMap.put("maxLoadCount","-1");
					infoMap.put("defaultTimeOut","-1");
					infoMap.put("defaultNotInUseTime","-1");

					//设置数据源信息
					((DataManager)dm).addElementInfoMap(infoMap);

					//构建查询类
					IDBQuery dbq = new DBQuery();
					((DBQuery)dbq).setSN(getSn());
					((IBean)dbq).setBeanFactory(_beanFactory);
					((DBQuery)dbq).setDataManager(dm);
					dbq.setDataSource(memDbSource);
					((IBase)dbq).setBase(this);

					memDbs = new MemoryDbService(this,dbq);

					_beanFactory.setObject(MemoryDbService.class,memDbs);
				}
			}catch(Exception e) {
				e.printStackTrace();
				return null;
			}
		}
		return memDbs;
	}
	
	/**
	 * 关闭（释放）临时表
	 * @param tableName 表名
	 * 2019年11月5日
	 * @author MBG
	 */
	public void memClose(String tableName) {
		MemoryDbService mem = memDbs(); //获取内存数据库服务类
		if(mem==null) {
			return;
		}
		mem.close(tableName);
	}
	
	/**
	 * 更新表状态
	 * @param  dsName    数据源主键
	 * @param  tableName 表名
	 * @return           新的表状态值
	 * 2017年10月12日
	 * @author MBG
	 */
	public String updateTableState(String dsName,String tableName){
		return db(dsName).setTableState(tableName);
	}

	/**
	 * 获取指定表的状态值
	 * @param  dsName     数据源主键
	 * @param  tableName  表名
	 * @return            当前表状态值
	 * 2017年10月12日
	 * @author MBG
	 */
	public String tableState(String dsName,String tableName) {
		return db(dsName).getTableState(tableName);
	}

	/**
	 * 获取数据库连接信息容器
	 */
	public Map<String,Map<String,String>> getDbInfoMap(){
		try {
			//数据库管理类
			IDataManager dm = getBean(IDataManager.class);
			return dm.getElementInfoMap();
		}catch(Exception e) {
			e.printStackTrace();
		}
		return new HashMap<String,Map<String,String>>();
	}
	
	/**
	 * 设置个新的数据库连接信息
	 * @param dsName   数据源主键
	 * @param infoMap  数据源信息
	 * 2019年3月6日
	 * @author MBG
	 */
	public void putDbInfoMap(String dsName,Map<String,String> infoMap) {
		if(dsName==null || dsName.length()<1) {
			return;
		}
		infoMap.put("name",dsName);
		if(!infoMap.containsKey("jndiName")) {
			infoMap.put("jndiName",infoMap.remove("jndi_name"));
		}
		if(!infoMap.containsKey("sourceType")) {
			infoMap.put("sourceType",infoMap.remove("source_type"));
		}
		if(!infoMap.containsKey("databaseUrl")) {
			infoMap.put("databaseUrl",infoMap.remove("db_url"));
		}
		if(!infoMap.containsKey("user")) {
			infoMap.put("user",infoMap.remove("db_user"));
		}
		if(!infoMap.containsKey("password")) {
			infoMap.put("password",infoMap.remove("db_pwd"));
		}
		if(!infoMap.containsKey("db_source")) {
			infoMap.put("db_source",dsName);
		}
		String value = str(infoMap.get("driverClassName"));
		if(value.length()<1) {
			value = str(infoMap.remove("db_driver_class_name"));
			if(value.length()<1) {
				value = "oracle.jdbc.OracleDriver";
			}
			infoMap.put("driverClassName",value);
		}
		value = str(infoMap.get("defaultTimeOut"));
		if(value.length()<1) {
			value = str(infoMap.remove("db_time_out"));
			if(value.length()<1) {
				value = "300000";
			}
			infoMap.put("defaultTimeOut",value);
		}
		value = str(infoMap.get("defaultNotInUseTime"));
		if(value.length()<1) {
			value = str(infoMap.remove("db_not_in_use_time"));
			if(value.length()<1) {
				value = "600000";
			}
			infoMap.put("defaultNotInUseTime",value);
		}
		value = str(infoMap.get("maxLoadCount"));
		if(value.length()<1) {
			value = str(infoMap.remove("db_max_load_count"));
			if(value.length()<1) {
				value = "200";
			}
			infoMap.put("maxLoadCount",value);
		}
		
		value = str(infoMap.get("autocommit"));
		if(value.length()<1) {
			infoMap.put("autocommit",infoMap.remove("db_auto_commit"));
		}

		value = str(infoMap.get("replaceFilePathChar"));
		if(value.length()<1) {
			infoMap.put("replaceFilePathChar",infoMap.remove("replace_file_path_char"));
		}
		try {
			//数据库管理类
			IDataManager dm = getBean(IDataManager.class);
			//设置数据源信息
			((DataManager)dm).addElementInfoMap(infoMap);
		}catch(Exception e) {
			e.printStackTrace();
			return;
		}
	}

	/**
	 * 判断是否存在指定数据源配置
	 * @param source 数据源主键
	 */
	public boolean isdb(String source){
		//尝试从配置文件中获取该数据源的驱动类路径
		String info = prop.getParameter("ws/db/"+source+"/driver");
		if(info.length()<1){
			//尝试获取别名
			info = prop.getParameter("ws/db/"+source);
            return info.length() >= 1;
		}
		return true;
	}

	/**
	 * 获取指定数据源的数据库操作类
	 * @param source 数据源
	 * @return 数据库操作类实例
	 */
	public synchronized IDBQueryExt db(String source) {
		//获取构建好的类实例
		IDBQuery dbq = dbMap.get(source);
		boolean outDbLog = true; //是否输出日志
		if(dbq==null) {
			synchronized(source) {
				//获取类加载器
				try {
					//数据库管理类
					IDataManager dm = getBean(IDataManager.class);
					
					if(!dm.getElementInfoMap().containsKey(source)) {
						//数据源信息块
						IViewHandler sourceVh = prop.getParameterXml("ws/db/"+source);
						if(sourceVh.isEmpty()){
							warning("没有发现该数据源:["+source+"]的配置信息");
							return null;
						}
						//当前数据信息配置是虚拟的，指向sourceVh   比如：<dev>gm</dev> 指向gm数据源
						String dbSource = null;
						if(sourceVh.getChildDealNodes().size()<1) {
							dbSource = sourceVh.nt();
							sourceVh = prop.getParameterXml("ws/db/"+dbSource);
							if(sourceVh.isEmpty()){
								warning("没有发现该数据源:["+source+"]的配置信息");
								return null;
							}
						}else {
							dbSource = source;
						}
						//经过转换主键的数据库信息容器
						HashMap<String,String> infoMap = new HashMap<String,String>();
						infoMap.put("name",source);
						infoMap.put("title",sourceVh.a("title"));
						infoMap.put("databaseUrl",sourceVh.fnn("url").nt());
						infoMap.put("user",sourceVh.fnn("user").nt());
						infoMap.put("password",JCECrpUtil.checkDecode(sourceVh.fnn("pwd").nt()));
            infoMap.put("paraStatementType",sourceVh.fnn("paraStatementType").nt()); //在构建事务类时的类型 mbg 2020-11-24 针对 impala数据源，不能传入默认参数的问题

						/*
						 * 设置默认值
						 */
						String info = sourceVh.fnn("db_source").nt();
						if(info.length()<1) {
							info = source;
						}
						infoMap.put("db_source",info); //设置当前数据源未别名，真实数据源主键名为该值
						
						//设置JNDI模式时 命名空间的名字，如果设置了该值，下面的值可以不用设置
						//也可以设置登录名和密码。
						//必须设置数据库类型
						info = sourceVh.fnn("jndiName").nt();
						if(info.length()<1) {
							info = sourceVh.fnn("jndi_name").nt();
						}
						if(info.length()>0) {
							infoMap.put("jndiName",info);
						}
						//在JNDI模式中，无法识别目标数据库类型，需要指定目标数据库的类型
						//数据库类型，目前支持：oracle,h2,mssql,mysql
						info = sourceVh.fnn("sourceType").nt();
						if(info.length()<1) {
							info = sourceVh.fnn("source_type").nt();
						}
						if(info.length()>0) {
							infoMap.put("sourceType",info);
						}
						info = sourceVh.fnn("driver").nt();
						if(info.length()<1) {
							infoMap.put("driverClassName","oracle.jdbc.OracleDriver");
						}else {
							infoMap.put("driverClassName",info);
						}
						info = sourceVh.fnn("timeout").nt();
						if(info.length()<1) {
							infoMap.put("defaultTimeOut","120000");
						}else {
							infoMap.put("defaultTimeOut",info);
						}
						info = sourceVh.fnn("notinuse").nt();
						if(info.length()<1) {
							infoMap.put("defaultNotInUseTime","300000");
						}else {
							infoMap.put("defaultNotInUseTime",info);
						}
						info = sourceVh.fnn("maxloadcount").nt();
						if(info.length()<1) {
							infoMap.put("maxLoadCount","20");
						}else {
							infoMap.put("maxLoadCount",info);
						}
						info = sourceVh.fnn("autocommit").nt();
						if(info.length()<1) {
							infoMap.put("autocommit","true");
						}else {
							infoMap.put("autocommit",info);
						}
						//在建立数据库连接时执行的语句
						info = sourceVh.fnn("init_script").nt();
						if(info.length()>0) {
							infoMap.put("initScript",info);
						}
						//连接access数据库用到的，已经废弃
						info = sourceVh.fnn("replace_file_path_char").nt();
						if(info.length()>0) {
							infoMap.put("replaceFilePathChar",info);
						}
						
						//日志是否不输出到控制台
						outDbLog = !boo(sourceVh.fnn("db_log_no_out_console").nt());
						infoMap.put("db_log_no_out_console",outDbLog?"false":"true");
						
						//日志是否写入自定义日志文件  该值是自定义日志文件的文件名（不包含扩展名）
						infoMap.put("db_log_file_key",sourceVh.fnn("db_log_file_key").nt());
						
						//设置数据源信息
						((DataManager)dm).addElementInfoMap(infoMap);
					}
					if(outDbLog) {
						log("准备初始化数据源：["+source+"]");
					}
					dbq = new DBQuery();
					((DBQuery)dbq).setSN(getSn());
					((IBean)dbq).setBeanFactory(_beanFactory);
					((DBQuery)dbq).setDataManager(dm);
					dbq.setDataSource(source);
					((IBase)dbq).setBase(this);
				}catch(Exception e) {
					e.printStackTrace();
					return null;
				}
				dbq.setDataSource(source); //设置数据源
				dbMap.put(source,dbq);
				if(outDbLog) {
					log("初始化数据源：["+source+"]完毕");
				}
			}
		}
		return new DBQueryExt(dbq);
	}


	/**
	 * 在脚本执行发生异常时执行该方法
	 * 
	 * paras参数，在脚本中固定为 Object[] _SUB_PATAS，
	 * 在脚本过程中可以构造这个参数的实例，
	 * 比如 _SUB_PATAS = new Object[]{"1","2"};
	 * 
	 * 注意：如果脚本（这个脚本带返回值）执行发生异常，并且用该方法拦截到了异常，希望
	 *      该脚本并不抛出异常，而是在这个方法中做了异常处理，希望能返回一个处理后的返回值，
	 *      这个返回值需要放到传入参数 in 中，key为 _RETURN_VALUE_
	 * 
	 * @param e          异常类实例
	 * @param _in        当前脚本传入参数容器
	 * @parma paras      当前脚本执行过程中声明的参数
	 * @return           返回true，不再往上抛异常 返回false，继续往上抛异常
	 * @throws Exception 执行发生异常
	 * 2019年12月6日
	 * @author MBG
	 */
	@Override
	public boolean doException(Exception e,Map<String,?> _in,Object[] paras) throws Exception {
		return false;
	}

	/**
	 * 在脚本执行结束后执行该方法
	 * 
	 * paras参数，在脚本中固定为 Object[] _SUB_PATAS，
	 * 在脚本过程中可以构造这个参数的实例，
	 * 比如 _SUB_PATAS = new Object[]{"1","2"};
	 * 
	 * @param _in        当前脚本传入参数容器
	 * @parma paras      当前脚本执行过程中声明的参数
	 * @throws Exception 异常
	 * 2019年12月6日
	 * @author MBG
	 */
	@Override
	public void doFinally(Map<String,?> _in,Object[] paras) throws Exception {}
}
